package cn.handwrit.springframework.spring;

import java.beans.Introspector;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.concurrent.ConcurrentHashMap;

public class SpringApplicationContext {

    private Class configClass;

    private ConcurrentHashMap<String, BeanDefinition> BeanDefinitionMap = new ConcurrentHashMap<>();
    private ConcurrentHashMap<String, Object> singletonMap = new ConcurrentHashMap<>();
    private ArrayList<BeanPostProcessor> beanPostProcessorArrayList = new ArrayList<>();

    public SpringApplicationContext(Class appConfigClass) {
        this.configClass = appConfigClass;
        //扫描当前路径

        try {
            if (appConfigClass.isAnnotationPresent(ComponentScan.class)) {
                ComponentScan componentScan = (ComponentScan) appConfigClass.getAnnotation(ComponentScan.class);
                String puth = componentScan.value();
                if ("".equals(puth)) {

                } else {
                    String path = puth.replace(".", "/");
                    ClassLoader classLoader = SpringApplicationContext.class.getClassLoader();
                    URL resource = classLoader.getResource(path);
                    File file = new File(resource.getFile());
                    if (file.isDirectory()) {
                        for (File f : file.listFiles()) {
                            String absolutePath = f.getAbsolutePath();
                            if (absolutePath.endsWith(".class")) {
                                String clazzName = absolutePath.substring(absolutePath.lastIndexOf("\\"), absolutePath.indexOf(".class"));
                                clazzName = clazzName.replace("\\", "");
                                Class<?> aClass = classLoader.loadClass(puth + "." + clazzName);
                                if (aClass.isAnnotationPresent(Component.class)) {
                                    //beanPostProcessor收集
                                    if (BeanPostProcessor.class.isAssignableFrom(aClass)) {
                                        Object o = aClass.getConstructor().newInstance();
                                        beanPostProcessorArrayList.add((BeanPostProcessor)o);
                                    }
                                    //我们认为这是需要容器管理的bean
                                    //BeanDefinition
                                    BeanDefinition beanDefinition = new BeanDefinition();
                                    Component component = aClass.getAnnotation(Component.class);
                                    String beanName = component.value();
                                    if ("".equals(beanName)) {
                                        //除了前两个字符是大写的情况，其他情况都会把第一个字符转为小写返回
                                        beanName = Introspector.decapitalize(clazzName);
                                    }
                                    if (aClass.isAnnotationPresent(Scope.class)) {
                                        Scope scope = aClass.getAnnotation(Scope.class);
                                        beanDefinition.setScope(scope.value());
                                    } else {
                                        beanDefinition.setScope("singleton");
                                    }
                                    beanDefinition.setType(aClass);
                                    BeanDefinitionMap.put(beanName, beanDefinition);
                                }
                            }
                        }
                    }
                }

            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        BeanDefinitionMap.keySet().forEach(key -> {
            BeanDefinition beanDefinition = BeanDefinitionMap.get(key);
            if (!singletonMap.containsKey(key)) {
                Object o = creatBean(key, beanDefinition);
                singletonMap.put(key, o);
            }
        });
        System.out.println(singletonMap);
    }

    private Object creatBean(String beanName, BeanDefinition beanDefinition) {
        try {
            Class clazz = beanDefinition.getType();
            Object o = clazz.getConstructor().newInstance();
            //Autowired实现
            for (Field field : clazz.getDeclaredFields()) {
                if (field.isAnnotationPresent(Autowired.class)) {
                    Object bean = getBean(field.getName());
                    field.setAccessible(true);
                    field.set(o,bean);
                }
            }
            //XXXXAwired实现
            if (o instanceof BeanNameAware) {
                ((BeanNameAware)o).setBeanName(beanName);
            }
            // 在源码中 这只是初始化前的其中一个步骤，源码中会走到applePostProcessBeforeInitialization方法
            //BeanPostProcessor实现
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorArrayList) {
                o = beanPostProcessor.postProcessBeforeInitialization(beanName,o);
            }

            //Bean生命周期初始化
            if (o instanceof InitializingBean) {
                ((InitializingBean)o).afterPropertiesSet();
            }
            // 在源码中 这只是初始化后的其中一个步骤，源码中会走到applePostProcessAfterInitialization方法
            //BeanPostProcessor实现
            // 在初始化后进行aop相关操作：生成代理对象
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorArrayList) {
                o = beanPostProcessor.postProcessAfterInitialization(beanName,o);
            }

            return o;
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        }
    }

    public Object getBean(String beanName) {
        if (BeanDefinitionMap.containsKey(beanName)) {
            BeanDefinition beanDefinition = BeanDefinitionMap.get(beanName);
            Class beanClass = beanDefinition.getType();
            if (beanClass.isAnnotationPresent(Scope.class)) {
                Scope scope = (Scope) beanClass.getAnnotation(Scope.class);
                if (scope.value().equals("singleton")) {
                    Object o = singletonMap.get(beanName);
                    if (o == null){
                        o = creatBean(beanName, beanDefinition);
                        singletonMap.put(beanName,o);
                    }
                    return o;
                } else {
                    return creatBean(beanName, beanDefinition);
                }
            } else {
                Object o = singletonMap.get(beanName);
                if (o == null){
                    o = creatBean(beanName, beanDefinition);
                    singletonMap.put(beanName,o);
                }
                return o;
            }
        }else {
            throw new NullPointerException();
        }

    }
}
